#include "gameworld.h"
#include "watertile.h"
#include "shipobject.h"

//be able to eventually define a different size World
GameWorld GameWorld::gameworld(8,8,5);
// medium difficulty
//GameWorld GameWorld::gameworld(10,10)
// hard difficulty
//GameWorld GameWorld::gameworld(20,20)

//returns object with <id>, or NULL if none
ShipObject GameWorld::getById(int id) {
    for (size_t i = 0; i < opponentships.size(); ++i) {
        ShipObject obj = opponentships.at(i);
        if (obj.getId() == id) {
            return obj;
        }
    }    
}

//return ship by type
ShipObject* GameWorld::getByShipType(QString shipType, QString whichPlayer) {
    //i think this code is improperly returning a pointer
//    if (whichPlayer == "ai") {
//        for (size_t i = 0; i < playerships.size(); ++i) {
//            ShipObject obj = playerships.at(i);
//            QString type = obj.getImage();
//            if (type.contains(shipType)) {
//                return &obj;
//            }
//        }
//    } else {
//        for (size_t i = 0; i < opponentships.size(); ++i) {
//            ShipObject obj = opponentships.at(i);
//            QString type = obj.getImage();
//            if (type.contains(shipType)) {
//                return &obj;
//            }
//        }
//    }
    ShipObject *ship;
    if (whichPlayer == "ai") {
        for (size_t i = 0; i < playerships.size(); ++i) {
            ship = &(playerships[i]);
            QString type = ship->getImage();
            if (type.contains(shipType)) {
                return ship;
            }
        }
    } else {
        for (size_t i = 0; i < opponentships.size(); ++i) {
            ship = &(opponentships[i]);
            QString type = ship->getImage();
            if (type.contains(shipType)) {
                return ship;
            }
        }
    }

}


GameWorld::~GameWorld() {
//    for (size_t i = 0; i < opponentships.size(); ++i) {
//        delete opponentships.at(i);
//    }
//    for (size_t i = 0; i < playerships.size(); ++i) {
//        delete playerships.at(i);
//    }
    for (size_t i = 0; i < watertiles.size(); ++i) {
        WaterTile* curtile = watertiles[i];
        delete curtile;
    }
    for (size_t i = 0; i < opponentwatertiles.size(); ++i) {
        WaterTile* curtile = opponentwatertiles[i];
        delete curtile;
    }
}

void GameWorld::resetWorld()
{
    playerships.clear();
    opponentships.clear();
    watertiles.clear();
    opponentwatertiles.clear();
    score = 0;
    numPlayerShips = 5;
    numOpponentShips = 5;
    cheatMode = false;
}

// factory: creates and returns a ShipObject or derived class,
// determined by <type>. Returns NULL if <type> is not recognized.
ShipObject GameWorld::createShip(const string& type, const int &row, const int &col, const string &img, const bool &portrait) {
    if (type.find("battleship") == 0)
        return ShipObject(row, col, img, 4, portrait);
    else if (type.find("aircraftcarrier") == 0)
        return ShipObject(row, col, img, 5, portrait);
    else if (type.find("cruiser") == 0)
        return ShipObject(row, col, img, 3, portrait);
    else if (type.find("destroyer") == 0)
        return ShipObject(row, col, img, 2, portrait);
    else if (type.find("submarine") == 0)
        return ShipObject(row, col, img, 3, portrait);
    else if (type.find("frigate") == 0)
        return ShipObject(row, col, img, 3, portrait);
    else if (type.find("littoral") == 0)
        return ShipObject(row, col, img, 4, portrait);

}

// factory: creates and returns a WaterTile or derived class,
// determined by <type>. Returns NULL if <type> is not recognized.
WaterTile *GameWorld::createTile(const string& type, const int &row, const int &col, const string& img) {
    if (type.find("water") == 0)
        return new WaterTile(row, col, type, img);
    else
        return NULL;
}

WaterTile *GameWorld::createOpponentTile(const string& type, const int& row, const int& col, const string& img) {
    if (type.find("water") == 0)
        return new WaterTile(row, col, type, img);
    else
        return NULL;
}

void GameWorld::clearShipSelections() {
    for (size_t i = 0; i < opponentships.size(); ++i) {
        ShipObject curship = opponentships.at(i);
        curship.setSelected(false);
    }
}

void GameWorld::saveGame()
{
//    QDir savesDir("saves");
//    if (!(QDir("saves/" + username).exists())) {
//        QDir().mkdir("saves/" + username);
//    }


    QDir saveDir("saves/" + username);
    saveDir.setPath("saves");
    saveDir.mkdir(username);
    saveDir.cd(username);
    QFile file(saveDir.filePath("data.txt"));
    file.resize(0);
    file.open(QIODevice::WriteOnly);
//    QDataStream out(&file);
    QTextStream out(&file);
//    out.setVersion(QDataStream::Qt_4_8);


    QList<ShipObject> playershipstosave = getPlayerShips();
    QList<ShipObject> aishipstosave = getOpponentShips();
    vector<WaterTile*> playertilestosave = getWaterTiles();
    vector<WaterTile*> aitilestosave = getOpponentWaterTiles();
    QString numplayerships = QString::number(getNumShips("player"));
    QString numaiships = QString::number(getNumShips("ai"));
    QString score = QString::number(getScore());


    foreach (ShipObject tempship, playershipstosave) {
        out << QString::fromStdString(tempship.toString());
    }
    foreach (WaterTile* temptile, playertilestosave) {
        out << QString::fromStdString(temptile->toString());
    }
    foreach (ShipObject tempship, aishipstosave) {
        out << QString::fromStdString(tempship.toString());
    }
    foreach (WaterTile* temptile, aitilestosave) {
        out << QString::fromStdString(temptile->toString());
    }

    out << numplayerships << endl;
    out << numaiships << endl;
    out << score << endl;

    file.close();

}

void GameWorld::loadGame()
{    
    QFile file("saves/" + username + "/data.txt");
    file.open(QIODevice::ReadOnly);
    QTextStream in(&file);

    QList<QString> playershiplist;
    QList<QString> playertilelist;
    QList<QString> aishiplist;
    QList<QString> aitilelist;
    QString numplayerships;
    QString numaiships;
    QString score;

    for (size_t i = 0; i < (5*8); ++i) {    //5 ships with 8 pieces of data each
        QString curItem;
        in >> curItem;
        curItem = curItem.trimmed();
        playershiplist.push_back(curItem);
    }

    for (size_t i = 0; i < (64*7); ++i) {   //64 tiles with 7 pieces of data each
        QString curItem;
        in >> curItem;
        curItem = curItem.trimmed();
        playertilelist.push_back(curItem);
    }
    for (size_t i = 0; i < (5*8); ++i) {
        QString curItem;
        in >> curItem;
        curItem = curItem.trimmed();
        aishiplist.push_back(curItem);
    }
    for (size_t i = 0; i < (64*7); ++i) {
        QString curItem;
        in >> curItem;
        curItem = curItem.trimmed();
        aitilelist.push_back(curItem);
    }

    in >> numplayerships;
    in >> numaiships;
    in >> score;
    file.close();

    //add all the ships and tiles to the gameworld

    //add player ships
    for (size_t i = 0; i < playershiplist.size(); ++i) {
        int col = playershiplist[i].toInt();
        int row = playershiplist[i+1].toInt();
        QString image = playershiplist[i+2];
        bool selected = playershiplist[i+3].toInt();
        bool portrait = playershiplist[i+4].toInt();
        bool sunk = playershiplist[i+5].toInt();
        int numhits = playershiplist[i+6].toInt();
        int hp = playershiplist[i+7].toInt();

        ShipObject newship(row, col, image.toStdString(), hp, portrait);
        newship.setSelected(selected);
        newship.setSunk(sunk);
        newship.setNumHits(numhits);
        addPlayerShip(newship);
        i = i + 7;  //skip to next ship
    }

    //add player tiles
    for (size_t i = 0; i < playertilelist.size(); ++i) {
        string type = playertilelist[i].toStdString();
        int col = playertilelist[i+1].toInt();
        int row = playertilelist[i+2].toInt();
        string image = playertilelist[i+3].toStdString();
        bool selected = playertilelist[i+4].toInt();
        string typeship = playertilelist[i+5].toStdString();
        bool hit = playertilelist[i+6].toInt();

        WaterTile *newtile = new WaterTile(row, col, type, image);
        newtile->setSelected(selected);
        newtile->setTypeShip(QString::fromStdString(typeship));
        newtile->setHit(hit);
        addTile(newtile);
        i = i + 6;
    }


    //add ai ships
    for (size_t i = 0; i < aishiplist.size(); ++i) {
        int col = aishiplist[i].toInt();
        int row = aishiplist[i+1].toInt();
        QString image = aishiplist[i+2];
        bool selected = aishiplist[i+3].toInt();
        bool portrait = aishiplist[i+4].toInt();
        bool sunk = aishiplist[i+5].toInt();
        int numhits = aishiplist[i+6].toInt();
        int hp = aishiplist[i+7].toInt();

        ShipObject newship(row, col, image.toStdString(), hp, portrait);
        newship.setSelected(selected);
        newship.setSunk(sunk);
        newship.setNumHits(numhits);
        addShip(newship);
        i = i + 7;
    }

    //add ai tiles
    for (size_t i = 0; i < aitilelist.size(); ++i) {
        string type = aitilelist[i].toStdString();
        int col = aitilelist[i+1].toInt();
        int row = aitilelist[i+2].toInt();
        string image = aitilelist[i+3].toStdString();
        bool selected = aitilelist[i+4].toInt();
        string typeship = aitilelist[i+5].toStdString();
        bool hit = aitilelist[i+6].toInt();

        WaterTile *newtile = new WaterTile(row, col, type, image);
        newtile->setSelected(selected);
        newtile->setTypeShip(QString::fromStdString(typeship));
        newtile->setHit(hit);
        addOpponentTile(newtile);
        i = i + 6;
    }

    //set numships and score
    setNumShips("player", numplayerships.toInt());
    setNumShips("ai", numaiships.toInt());
    setScore(score.toInt());


}

bool GameWorld::Fire(WaterTile *tile, QString whichPlayer){
    bool shipsunk = false;
    vector<WaterTile*>& templist = opponentwatertiles;
    if (!tile->isHit()) {
        if (tile->getType() == "ship") {
            QString shipType = tile->getTypeShip();
            ShipObject *ship;
//            if (whichPlayer == "player")
                ship = getByShipType(shipType, whichPlayer);
//            else
//                ship = getByShipType(shipType, whichPlayer);

            //if (whichPlayer == "player")
            // ++score;
            ship->decHp();
            ship->incrementNumHits();
            tile->setImage("ship_hit");
            if (ship->getHp() == 0) {
                ship->sinkShip();
                shipsunk = true;
            }
        } else if (tile->getType() == "water") {
            tile->setImage("watertile_miss");
        }
        if (whichPlayer == "player"){
            score ++;
        }
        tile->setHit(true);        
    }

    return shipsunk;
}

void GameWorld::decShips(QString whichPlayer)
{
    if (whichPlayer == "player") {
        --numPlayerShips;
    } else {
        --numOpponentShips;
    }
}

int GameWorld::getNumShips(QString whichPlayer)
{
    if (whichPlayer == "player")
        return numPlayerShips;
    else
        return numOpponentShips;
}

void GameWorld::setNumShips(QString whichplayer, int newnum)
{
    if (whichplayer == "player") {
        numPlayerShips = newnum;
    } else {
        numOpponentShips = newnum;
    }
}
